package com.example.es.service.impl;

import com.example.es.entity.Product;
import com.example.es.repository.ProductRepository;
import com.example.es.rest.vo.AggVO;
import com.example.es.rest.vo.QueryVO;
import com.example.es.service.ProductService;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.InternalNested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;

/**
 * @Title:
 * @Author: wangsai
 * @Date: 2020/1/20 15:58
 * @Description:
 */
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductRepository productRepository;


    @Override
    public Product save(Product product) {
        return productRepository.save(product);
    }

    @Override
    public List<Product> finAll() {
        Iterable<Product> products = productRepository.findAll();
        return Lists.newArrayList(products);
    }

    // 商品分页查询
    @Override
    public Page<Product> search(QueryVO query, int page, int size) {
        Pageable pageable = PageRequest.of(page-1, size);
        return productRepository.search(this.addFilters(query), pageable);
    }

    // 过滤条件
    private BoolQueryBuilder addFilters(QueryVO query) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        List<AggVO.AttrGroup> attrGroups = query.getAttrs();
        if (attrGroups != null) {
            // 多属性过滤查询，如（内存、颜色、屏幕尺寸、版本等等）
            for (AggVO.AttrGroup attrGroup : attrGroups) {
                BoolQueryBuilder attrBoolQuery = QueryBuilders.boolQuery();
                // 匹配商品规格参数名
                attrBoolQuery.filter(QueryBuilders.matchQuery(Product.FieldName.ATTRS_NAME, attrGroup.getAttrName()));
                // 匹配商品规格参数值，这里多值匹配用【termsQuery】注意区分【termQuery】
                attrBoolQuery.filter(QueryBuilders.termsQuery(Product.FieldName.ATTRS_VALUE, attrGroup.getAttrValues()));
                // 使用NestedQuery查询（嵌套对象的过滤查询）
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(Product.FieldName.ATTRS, attrBoolQuery, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }
        }
        if (query.getId() != null) {
            // 商品Id查询
            boolQueryBuilder.filter(QueryBuilders.matchQuery(Product.FieldName.ID, query.getId()).operator(Operator.AND));
        }
        if (!query.getBrands().isEmpty()) {
            // 品牌查询
            boolQueryBuilder.filter(QueryBuilders.termsQuery(Product.FieldName.BRAND, query.getBrands()));
        }
        if (query.getCategory() != null) {
            // 分类查询
            boolQueryBuilder.filter(QueryBuilders.matchQuery(Product.FieldName.CATEGORY, query.getCategory()).operator(Operator.AND));
        }
        if (StringUtils.isNotBlank(query.getName())) {
            // 商品名称查询（这里暂时没做分词处理）
            boolQueryBuilder.filter(QueryBuilders.matchQuery(Product.FieldName.NAME, query.getName()).operator(Operator.AND));
        }
        return boolQueryBuilder;
    }


    // 筛选条件聚合查询接口
    @Override
    public AggVO agg(QueryVO query) {
        Pageable pageable = PageRequest.of(0, 10);
        //检索条件
        BoolQueryBuilder boolQueryBuilder = this.addFilters(query);
        final String BRAND_AGG = "brand_agg";
        final String CATEGORY_AGG = "category_agg";
        final String ATTR_AGG = "attr_agg";
        final String ATTR_NAME_AGG = ATTR_AGG + "_name";
        final String ATTR_VALUE_AGG = ATTR_AGG+"_value";
        //聚合条件

        // 品牌聚合（取命中最多的前500个品牌）
        TermsAggregationBuilder brandAggBuilder = AggregationBuilders.terms(BRAND_AGG).field(Product.FieldName.BRAND).size(500);
        // 分类聚合（取命中最多的前20个）
        TermsAggregationBuilder categoryAggBuilder = AggregationBuilders.terms(CATEGORY_AGG).field(Product.FieldName.CATEGORY).size(20);
        // 内置对象-规格聚合
        NestedAggregationBuilder attrAggBuilder = AggregationBuilders.nested(ATTR_AGG, Product.FieldName.ATTRS);
        // 先聚合规格名称
        TermsAggregationBuilder attrNameAggBuilder = AggregationBuilders.terms(ATTR_NAME_AGG).field(Product.FieldName.ATTRS_NAME);
        // 再将名称分组后聚合规格值（取命中最多的前7个规格，聚合分组规格后再在每个分组规格中取命中最多的前15个参数值）
        attrNameAggBuilder.subAggregation(AggregationBuilders.terms(ATTR_VALUE_AGG).field(Product.FieldName.ATTRS_VALUE).size(15)).size(7);
        attrAggBuilder.subAggregation(attrNameAggBuilder);
        //构建查询
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder) // 过滤条件
                .addAggregation(brandAggBuilder) // 品牌聚合
                .addAggregation(categoryAggBuilder) // 分类聚合
                .addAggregation(attrAggBuilder) // 规格参数聚合
                .withPageable(pageable) // 分页
                .build();
        AggregatedPage<Product> search = (AggregatedPage)productRepository.search(searchQuery);
        AggVO aggVO = new AggVO();
        // 符合条件的商品总数量
        aggVO.setTotalNum(search.getTotalElements());

        // 品牌聚合结果
        Terms brandAgg = (Terms)search.getAggregation(BRAND_AGG);
        for (Terms.Bucket bucket : brandAgg.getBuckets()) {
            String brandName = bucket.getKeyAsString();
            aggVO.getBrands().add(brandName);
        }

        // 分类聚合结果
        Terms categoryAgg = (Terms)search.getAggregation(CATEGORY_AGG);
        for (Terms.Bucket bucket : categoryAgg.getBuckets()) {
            long categoryId = bucket.getKeyAsNumber().longValue();
            aggVO.getCategories().add(categoryId);
        }

        // 规格参数聚合结果
        InternalNested attrNested = (InternalNested)search.getAggregation(ATTR_AGG);
        Map<String, Aggregation> aggregationMap = attrNested.getAggregations().asMap();
        Terms attrNameAgg = (Terms)aggregationMap.get(ATTR_NAME_AGG);
        for (Terms.Bucket bucket : attrNameAgg.getBuckets()) {
            String attrName = bucket.getKeyAsString();
            AggVO.AttrGroup attrGroup = new AggVO.AttrGroup();
            attrGroup.setAttrName(attrName);
            Terms attrValueAgg = (Terms)bucket.getAggregations().asMap().get(ATTR_VALUE_AGG);
            for (Terms.Bucket subBucket : attrValueAgg.getBuckets()) {
                String attrValue = subBucket.getKeyAsString();
                attrGroup.getAttrValues().add(attrValue);
            }
            aggVO.getAttrs().add(attrGroup);
        }
        search.getAggregation(CATEGORY_AGG);
        return aggVO;
    }
}
